New handlers: refactored, Web API and AWS Lambda support#380
New handlers: refactored, Web API and AWS Lambda support#380witoszekdev wants to merge 61 commits intomainfrom
Conversation
🦋 Changeset detectedLatest commit: 534b64f The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
lkostrowski
left a comment
There was a problem hiding this comment.
It would be easier for me to review if I already see docs with examples. In case of SDK public API is more important than the code
| @@ -0,0 +1,5 @@ | |||
| --- | |||
| "@saleor/app-sdk": minor | |||
There was a problem hiding this comment.
please make it major since it has breaking changes
| "@saleor/app-sdk": minor | ||
| --- | ||
|
|
||
| Added `handlers/fetch-api` which adds support for frameworks that use [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) |
There was a problem hiding this comment.
please remember to add full changeset and docs
There was a problem hiding this comment.
| "./handlers/next-app-router": { | ||
| "types": "./handlers/fetch-api/index.d.ts", | ||
| "import": "./handlers/fetch-api/index.mjs", | ||
| "require": "./handlers/fetch-api/index.js" | ||
| }, |
There was a problem hiding this comment.
won't it have the same names as fetch api? It would be confusing, probably better to also add alias on export
so next-app-router file can include export { FetchApi as NextAppRouterWebhook } from '../fetch-api' or something like this
There was a problem hiding this comment.
would be easier if we see docs at this point
There was a problem hiding this comment.
This is fine, since exported names do not reference Fetch API in any way, names are the same as in Next.js handlers except they are in a different folder. Our exports are:
createAppRegisterHandlercreateManifestHandlercreateProtectedHandlerSaleorSyncWebhookSaleorAsyncWebhook
lkostrowski
left a comment
There was a problem hiding this comment.
another partial review ;)
There was a problem hiding this comment.
what is "fetch middleware"? If this is a middleware using Fetch Request API, I don't think we should call it that way. Maybe just "middleware"?
| @@ -0,0 +1,5 @@ | |||
| export type SaleorRequest = Request & { context?: Record<string, any> }; | |||
There was a problem hiding this comment.
probably you should avoid any in favor of unknown
There was a problem hiding this comment.
please add tests to all middlewares
|
|
||
| const debug = createFetchMiddlewareDebug("withRegisteredSaleorDomainHeader"); | ||
|
|
||
| export const withRegisteredSaleorDomainHeader: FetchMiddleware = (handler) => async (request) => { |
There was a problem hiding this comment.
we are actually checking "saleorApiUrl", not domain, and it 1.0.0 we will drop domain checking
| ); | ||
| } | ||
|
|
||
| const authData = await saleorApp?.apl.get(saleorApiUrl); |
There was a problem hiding this comment.
maybe can we extract this? Middleware checks header but also checks if auth data exists. And doesnt do anything with it. It doesnt event attach it to request, so it's cached
|
|
||
| const debug = createFetchMiddlewareDebug("withSaleorDomainPresent"); | ||
|
|
||
| export const withSaleorDomainPresent: FetchMiddleware = (handler) => async (request) => { |
There was a problem hiding this comment.
we can remove this, in v1 we can remove domain header, so no need to check this
| const checksToRun = [ | ||
| this.adapterMiddleware.withMethod(["POST"]), | ||
| this.adapterMiddleware.withSaleorDomainPresent(), | ||
| ]; |
There was a problem hiding this comment.
Im not sure if middleware should work as "check" only. If you check against e.g. existence of AuthData, you can pass Request and middleware can enrich it with context - eg attach AuthData there
There was a problem hiding this comment.
I don't think modifying Request object is a good idea. It would be like adding new properties to globals like Array or Window 😬 These objects are meant to be immutable.
Adding context is usually done by web frameworks that use Fetch API by creating new Context object and passing it everywhere, or by using AsyncLocalStorage. Example in Hono.
We could do this, but then we would need to have some context mechanism of our own. We could also use AsyncLocalStorage, but this is also problematic since runtimes like Cloudflare Workers, or Deno require enabling Node compatibility layer to do this :/
There was a problem hiding this comment.
Modifying request isn't anything new. It's not about modifying global, but instance. It's not about Array.prototype.foo by (new Array()).foo
Context you mentioned is a platform context.
What I mean is request context. It can be achieved several ways, eg with async_hooks, modifying request or adding another argument (like createWebhookHandler(req, res, ctx)). Or how TRPC middleware works.
You can also read eg https://expressjs.com/en/guide/using-middleware.html
What I want to achieve is to allow middleware to attach data for next middlewares or handlers and compose the rich request in the end.
For example, if your handler fetches AuthData, it can attach it to request (pass to next step), instead of fetching it again in next middleware/handler
There was a problem hiding this comment.
OK, I think we are talking about different things here and maybe the name middleware in this place is wrong and confusing 😅
These "middlewares" do not have any data we would want to pass as context, since they don't do any asynchronous actions.
This specific register action has a concept of "context" in its config, where you can attach to different events, e.g.:
/**
* Run right after Saleor calls this endpoint
*/
onRequestStart?(
request: RequestType,
context: {
authToken?: string;
saleorDomain?: string;
saleorApiUrl?: string;
respondWithError: CallbackErrorHandler;
}
): Promise<void>;Config shape is the same as previous Next.js actions in app-sdk, it just has different Request object type depending on platform
createProtectedHandler also has its own context:
export type ProtectedHandlerContext = {
baseUrl: string;
authData: AuthData;
user: TokenUserPayload;
};Webhook handlers also have context (no change from previous public API):
export type SaleorWebhookHandler<TPayload = unknown, TExtras = {}> = (
req: Request,
ctx: WebhookContext<TPayload> & TExtras
) => Response | Promise<Response>;As for fetch-middleware, please see my comment: #380 (comment).
| // TODO: We should strictly require `saleorApiUrl` instead of | ||
| // relying on `saleor-domain` that is deprecated |
There was a problem hiding this comment.
why? the comment is still valid because we are not doing strict check on saleorApiUrl for compatibility reasons (I didn't change bussiness logic, rather I just copied our old code and adapted it to work with our new adapter abstraction).
We can make another breaking change and just drop the Saleor-Domain usage altogether imho
|
@lkostrowski Public API usage doesn't change, the change made to existing Next.js handlers are meant to be drop-in and non-breaking. New handlers for Web API and Lambda have the same signature as Next.js handlers, they only differ when accepting a The only breaking change is removing one parameter, which I think was a mistake in our API design, since we were passing I've prepared examples here:
And docs here: saleor/saleor-docs#1437 |
|
@lkostrowski one more thing: I'm also not sure if we even want to add these middlewares to begin with? They are not used in other places in app-sdk. Previously we had them because they were passed to retes. I also don't think these are used in any of our apps, since most of them use higher level abstractions by using |
|
Released snapshot build with Install it with: pnpm add @saleor/app-sdk@0.0.0-pr-20250127113254 |
|
This PR will be split into multiple smaller ones for easier review |
|
All features are now merged to |
Description
This PR introduces new handlers for Fetch Web API and AWS Lambda and refactors existing code so that it can easily allow adding new platforms in the future.
After the change handlers code is splits them into 3 parts:
src/handlers/nextjs- For handlers that use Next.js platform adaptersrc/handlers/fetch-api- For handlers that use Fetch API platform adapterThis way we'll be able to easily add new platforms and handlers without changing existing code.
Currently in this PR:
Examples and docs
Usage examples:
https://github.com/witoszekdev/saleor-app-hono-aws-lambda-template
https://github.com/witoszekdev/saleor-app-hono-deno-template
https://github.com/witoszekdev/saleor-app-hono-cf-pages-template
saleor/saleor-app-template#267
Docs:
saleor/saleor-docs#1437
Breaking changes?
SaleorWebhookacceptsonErrorandformatErrorResponseparameters callbacks, now they won't passresobject in Next.js runtime, since these methods weren't supposed to send responses